
module FIR_Filter
#(
    parameter iWIDTH=10,
    parameter oWIDTH=10,
    parameter mWIDTH=8,
	 parameter sWIDTH=24,
    parameter STAGE=64
)
(
    input wire signed [iWIDTH-1:0] i_SignalInput,
    input wire i_clk,
    input wire i_rst,
    input wire [mWIDTH-1:0] i_Coeff,    //config data
    input wire i_cclk,                  //config clock
    input wire i_config_en,             //config enable
    output wire signed [oWIDTH-1:0] o_SignalOutput
);

//Config
reg signed [mWIDTH-1:0] Coefficient [STAGE:0];
reg [7:0] conf_cnt;
reg [7:0] conf_clkcnt;

always@(posedge i_cclk or posedge i_rst) begin
    if(i_rst) begin
    /*
        for ( conf_cnt=0 ; conf_cnt<=STAGE ; conf_cnt=conf_cnt+1 ) begin
            Coefficient[conf_cnt]<='b0;
        end
    */
Coefficient[0]<=	8'h	FF	;
Coefficient[1]<=	8'h	E1	;
Coefficient[2]<=	8'h	F6	;
Coefficient[3]<=	8'h	F3	;
Coefficient[4]<=	8'h	F2	;
Coefficient[5]<=	8'h	F0	;
Coefficient[6]<=	8'h	F0	;
Coefficient[7]<=	8'h	F0	;
Coefficient[8]<=	8'h	F0	;
Coefficient[9]<=	8'h	F1	;
Coefficient[10]<=	8'h	F3	;
Coefficient[11]<=	8'h	F6	;
Coefficient[12]<=	8'h	FA	;
Coefficient[13]<=	8'h	FE	;
Coefficient[14]<=	8'h	04	;
Coefficient[15]<=	8'h	0A	;
Coefficient[16]<=	8'h	11	;
Coefficient[17]<=	8'h	19	;
Coefficient[18]<=	8'h	21	;
Coefficient[19]<=	8'h	2A	;
Coefficient[20]<=	8'h	33	;
Coefficient[21]<=	8'h	3C	;
Coefficient[22]<=	8'h	46	;
Coefficient[23]<=	8'h	4F	;
Coefficient[24]<=	8'h	58	;
Coefficient[25]<=	8'h	60	;
Coefficient[26]<=	8'h	68	;
Coefficient[27]<=	8'h	6F	;
Coefficient[28]<=	8'h	75	;
Coefficient[29]<=	8'h	79	;
Coefficient[30]<=	8'h	7D	;
Coefficient[31]<=	8'h	7F	;
Coefficient[32]<=	8'h	7F	;
Coefficient[33]<=	8'h	7F	;
Coefficient[34]<=	8'h	7D	;
Coefficient[35]<=	8'h	79	;
Coefficient[36]<=	8'h	75	;
Coefficient[37]<=	8'h	6F	;
Coefficient[38]<=	8'h	68	;
Coefficient[39]<=	8'h	60	;
Coefficient[40]<=	8'h	58	;
Coefficient[41]<=	8'h	4F	;
Coefficient[42]<=	8'h	46	;
Coefficient[43]<=	8'h	3C	;
Coefficient[44]<=	8'h	33	;
Coefficient[45]<=	8'h	2A	;
Coefficient[46]<=	8'h	21	;
Coefficient[47]<=	8'h	19	;
Coefficient[48]<=	8'h	11	;
Coefficient[49]<=	8'h	0A	;
Coefficient[50]<=	8'h	04	;
Coefficient[51]<=	8'h	FE	;
Coefficient[52]<=	8'h	FA	;
Coefficient[53]<=	8'h	F6	;
Coefficient[54]<=	8'h	F3	;
Coefficient[55]<=	8'h	F1	;
Coefficient[56]<=	8'h	F0	;
Coefficient[57]<=	8'h	F0	;
Coefficient[58]<=	8'h	F0	;
Coefficient[59]<=	8'h	F0	;
Coefficient[60]<=	8'h	F2	;
Coefficient[61]<=	8'h	F3	;
Coefficient[62]<=	8'h	F6	;
Coefficient[63]<=	8'h	E1	;
Coefficient[64]<=	8'h	FF	;



        

    end
    else begin
        if(i_config_en) begin
            Coefficient[conf_clkcnt]<=i_Coeff;
        end
    end
end

always @(negedge i_cclk or posedge i_rst) begin
    if(i_rst) begin
        conf_clkcnt<='b0;
    end
    else begin
        if(i_config_en) begin
            conf_clkcnt<=conf_clkcnt+1;
        end
    end
end

//Generate Delay Pipeline
wire signed [mWIDTH-1:0] Delay_Pipeline [STAGE:0];
genvar i;
generate
for( i=0 ; i<=STAGE ; i=i+1 )begin: fir_pipline_generate_del
    if(i==0)begin: fir_first_input_reg_del
        FIR_SREG  
        #(  
            .WIDTH    (mWIDTH)
        )fir_pip_reg(
            .i_clk    (i_clk),
            .i_rst    (i_rst),
            .i_regin     (i_SignalInput[iWIDTH-1:(iWIDTH-mWIDTH)]),
            .o_regout    (Delay_Pipeline[i])
        );
    end
    else if(i==STAGE)begin: fir_last_output_reg_del
        FIR_SREG  
        #(  
            .WIDTH    (mWIDTH)
        )fir_pip_reg(
            .i_clk    (i_clk),
            .i_rst    (i_rst),
            .i_regin     (Delay_Pipeline[i-1]),
            .o_regout    (Delay_Pipeline[STAGE])
        );
    end
    else begin: fir_pipline_reg_del
        FIR_SREG   
        #(  
            .WIDTH    (mWIDTH)
        )fir_pip_reg(
            .i_clk (i_clk),
            .i_rst (i_rst),
            .i_regin  (Delay_Pipeline[i-1]),
            .o_regout (Delay_Pipeline[i])
        );
    end
end
endgenerate

//Generate Multiplier Pipeline
wire signed [(2*mWIDTH):0] Multi_Pipeline [STAGE:0];
wire [STAGE:0] MUL_MSB;
genvar j;
generate
for( j=0 ; j<=STAGE ; j=j+1 )begin: fir_pipline_generate_mul
    FIR_Multiplier
    #(  
        .IWID    (mWIDTH),
        .OWID    ((2*mWIDTH)+1)
    )fir_pip_mul(
        .i_clk      (i_clk),
        .i_rst      (i_rst),
        .i_mul1     (Delay_Pipeline[j]),
        .i_mul2     (Coefficient[j]),
        .o_product  (Multi_Pipeline[j]),
        .o_MSB      (MUL_MSB[j])
    );
end
endgenerate




//Generate Summator Pipeline
wire signed [sWIDTH-1:0] Sum_Pipeline [STAGE-1:0];
wire signed [(2*mWIDTH):0]Mul_tp;
genvar k;
generate
for( k=0 ; k<STAGE ; k=k+1 )begin: fir_pipline_generate_sum
    if(k==0) begin : fir_sum_stage0
        FIR_Summator
        #(  
            .IWID    (sWIDTH),
            .OWID    (sWIDTH)
        )fir_pip_sum(
            .i_clk      (i_clk),
            .i_rst      (i_rst),
            .i_add1     ({{(sWIDTH-(2*mWIDTH)){MUL_MSB[k]}},Multi_Pipeline[k]}),
            .i_add2     ({{(sWIDTH-(2*mWIDTH)){MUL_MSB[k+1]}},Multi_Pipeline[k+1]}),
            .o_sum      (Sum_Pipeline[k])
        );
    end
    else begin : fir_sum_stage
        FIR_Summator
        #(  
            .IWID    (sWIDTH),
            .OWID    (sWIDTH)
        )fir_pip_sum(
            .i_clk      (i_clk),
            .i_rst      (i_rst),
            .i_add1     (Sum_Pipeline[k-1]),
            .i_add2     ({{(sWIDTH-(2*mWIDTH)){MUL_MSB[k+1]}},Multi_Pipeline[k+1]}),
            .o_sum      (Sum_Pipeline[k])
        );
    end
end
endgenerate

wire signed [sWIDTH-1:0] FIR_Sum;
assign FIR_Sum=Sum_Pipeline[STAGE-1];
assign o_SignalOutput=FIR_Sum[sWIDTH-3:(sWIDTH-oWIDTH)-3];
endmodule

module FIR_SREG
#(
    parameter WIDTH = 8
)
(
    input wire [WIDTH-1:0] i_regin,
    input wire i_clk,
    input wire i_rst,
    output reg [WIDTH-1:0] o_regout
);

always@(posedge i_clk or posedge i_rst) begin
    if(i_rst) begin
        o_regout<='b0;
    end
    else begin
        o_regout<=i_regin;
    end
end

endmodule

module FIR_Multiplier
#(
    parameter IWID=8,
    parameter OWID=17
)
(
    input wire signed [IWID-1:0] i_mul1,
    input wire signed [IWID-1:0] i_mul2,
    input wire i_clk,
    input wire i_rst,
    output reg signed [OWID-1:0] o_product,
    output wire o_MSB
);
wire signed [OWID-1:0] mul_result;
assign mul_result=i_mul1*i_mul2;
always@(posedge i_clk or posedge i_rst) begin
        if(i_rst) begin
            o_product<='b0;
        end
        else begin
            o_product<=mul_result;
        end
    end
assign o_MSB=o_product[OWID-1];
endmodule

module FIR_Summator
#(
    parameter IWID=22,
    parameter OWID=22
)
(
    input wire signed [IWID-1:0] i_add1,
    input wire signed [IWID-1:0] i_add2,
    input wire i_clk,
    input wire i_rst,
    output reg signed [OWID-1:0] o_sum
);
wire signed [OWID-1:0] add_result;

    assign add_result=i_add1+i_add2;
	 //assign o_sum=add_result;

    always@(posedge i_clk or posedge i_rst) begin
        if(i_rst) begin
            o_sum<='b0;
        end
        else begin
            o_sum<=add_result;
        end
    end

endmodule

